home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 098 / grep.arc / GREP.C next >
C/C++ Source or Header  |  1986-08-18  |  18KB  |  682 lines

  1. /*
  2.  *
  3.  *
  4.  * The  information  in  this  document  is  subject  to  change
  5.  * without  notice  and  should not be construed as a commitment
  6.  * by Digital Equipment Corporation or by DECUS.
  7.  *
  8.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  9.  * assume any responsibility for the use or reliability of  this
  10.  * document or the described software.
  11.  *
  12.  *      Copyright (C) 1980, DECUS
  13.  *
  14.  * General permission to copy or modify, but not for profit,  is
  15.  * hereby  granted,  provided that the above copyright notice is
  16.  * included and reference made to  the  fact  that  reproduction
  17.  * privileges were granted by DECUS.
  18.  *
  19.  */
  20.  
  21. #include "stdio.h"
  22. #include "ctype.h"
  23.  
  24. /*
  25.  * grep.
  26.  *
  27.  * Runs on the Decus compiler or on vms.
  28.  * Converted for BDS compiler (under CP/M-80), 20-Jan-83, by Chris Kern.
  29.  *
  30.  * Converted to IBM PC with CI-C86 C Compiler June 1983 by David N. Smith
  31.  *
  32.  * Slightly modified and compiled under Datalight C v. 2.03, with a revised
  33.  * _main.obj, to take advantage of wild-card expansion, by David P. Babcock,
  34.  * August 1986.  (_main.obj as supplied would trap ? or anything with *)
  35.  *
  36.  * On vms, define as:
  37.  *
  38.  *      grep :== "$disk:[account]grep"     (native)
  39.  *      grep :== "$disk:[account]grep grep"     (Decus)
  40.  *
  41.  * See below for more information.
  42.  *
  43.  */
  44.  
  45.  
  46. char    *documentation[] = {
  47. "grep searches a file for a given pattern.  Execute by",
  48. "   grep [flags] regular_expression file_list",
  49. "",
  50. "Flags are single characters preceded by '-':",
  51. "   -c      Only a count of matching lines is printed",
  52. "   -f      Print file name for matching lines switch, see below",
  53. "   -n      Each line is preceded by its line number",
  54. "   -v      Only print non-matching lines",
  55. "",
  56. "The file_list is a list of files (wildcards are acceptable under MS-DOS",
  57. "2.0 or later).",
  58.  
  59. "",
  60. "The file name is normally printed if there is a file given.",
  61. "The -f flag reverses this action (print name no file, not if more).",
  62. "",
  63. 0 };
  64.  
  65.  
  66. char    *patdoc[] = {
  67. "The regular_expression defines the pattern to search for.  Upper- and",
  68. "lower-case are always ignored.  Blank lines never match.",
  69. "x      An ordinary character (not mentioned below) matches that character.",
  70. "        This may not be a space: a space may be specified only as a member",
  71. "        of the class :_ below.",
  72. "'\\'    The backslash quotes any char except space.  \"\\$\" matches a dollar-sign.",
  73. "'^'    A circumflex at the beginning of an expression matches the",
  74. "       beginning of a line.",
  75. "'$'    A dollar-sign at the end of an expression matches the end of a line.",
  76. "'.'    A period matches any character except \"new-line\".",
  77. "':a'   A colon matches a class of characters described by the following",
  78. "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,",
  79. "':n'     \":n\" matches alphanumerics, \":_\" matches spaces, tabs, and",
  80. "':_'     other control characters, such as new-line.",
  81. "'*'    An expression followed by an asterisk matches zero or more",
  82. "       occurrences of that expression: \"fo*\" matches \"f\", \"fo\"",
  83. "       \"foo\", etc.",
  84. "'+'    An expression followed by a plus sign matches one or more",
  85. "       occurrences of that expression: \"fo+\" matches \"fo\", etc.",
  86. "'-'    An expression followed by a minus sign optionally matches",
  87. "       the expression.",
  88. "'[]'   A string enclosed in square brackets matches any character in",
  89. "       that string, but no others.  If the first character in the",
  90. "       string is a circumflex, the expression matches any character",
  91. "       except \"new-line\" and the characters in the string.  For",
  92. "       example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"",
  93. "       matches \"abc\" but not \"axb\".  A range of characters may be",
  94. "       specified by two characters separated by \"-\".  Note that,",
  95. "       [a-z] matches alphabetics, while [z-a] never matches.",
  96. "The concatenation of regular expressions is a regular expression.",
  97. "A regular_expression may be enclosed in quotes to avoid parsing by the",
  98. " command-line wild-card expander.",
  99. 0};
  100.  
  101. #ifndef stdin
  102. #define stdin STDIN
  103. #define stdout STDOUT
  104. #define stderr STDERR
  105. /* #define DeSmet 1 */
  106. #endif
  107.  
  108. #define LMAX    512
  109. #define PMAX    256
  110.  
  111. #define CHAR    1
  112. #define BOL     2
  113. #define EOL     3
  114. #define ANY     4
  115. #define CLASS   5
  116. #define NCLASS  6
  117. #define STAR    7
  118. #define PLUS    8
  119. #define MINUS   9
  120. #define ALPHA   10
  121. #define DIGIT   11
  122. #define NALPHA  12
  123. #define PUNCT   13
  124. #define RANGE   14
  125. #define ENDPAT  15
  126.  
  127. int     cflag;
  128. int     fflag;
  129. int     nflag;
  130. int     vflag;
  131. int     nfile;
  132.  
  133. int     debug   =       0;         /* Set for debug code      */
  134.  
  135. char    *pp;
  136.  
  137. #ifndef vms
  138. char    file_name[81];
  139. #endif
  140.  
  141. char    lbuf[LMAX];
  142. char    pbuf[PMAX];
  143.  
  144. /*******************************************************/
  145.  
  146. main(argc, argv)
  147. int argc; char *argv[];
  148. {
  149.    register char   *p;
  150.    register int    c, i;
  151.    int             gotpattern;
  152.    int             gotcha;
  153.  
  154.    FILE            *f;
  155.  
  156.    if (argc <= 1)
  157.       usage("No arguments");
  158.       if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  159.       help(documentation);
  160.       help(patdoc);
  161.       return;
  162.       }
  163.    nfile = argc-1;
  164.    gotpattern = 0;
  165.    for (i=1; i < argc; ++i) {
  166.       p = argv[i];
  167.       if (*p == '-') {
  168.          ++p;
  169.          while (c = *p++) {
  170.             switch(tolower(c)) {
  171.  
  172.             case '?':
  173.                help(documentation);
  174.                break;
  175.  
  176.             case 'C':
  177.             case 'c':
  178.                ++cflag;
  179.                break;
  180.  
  181.             case 'D':
  182.             case 'd':
  183.                ++debug;
  184.                break;
  185.  
  186.             case 'F':
  187.             case 'f':
  188.                ++fflag;
  189.                break;
  190.  
  191.             case 'n':
  192.             case 'N':
  193.                ++nflag;
  194.                break;
  195.  
  196.             case 'v':
  197.             case 'V':
  198.                ++vflag;
  199.                break;
  200.  
  201.             default:
  202.                usage("Unknown flag");
  203.             }
  204.          }
  205.          argv[i] = 0;
  206.          --nfile;
  207.       } else if (!gotpattern) {
  208.          compile(p);
  209.          argv[i] = 0;
  210.          ++gotpattern;
  211.          --nfile;
  212.       }
  213.    }
  214.    if (!gotpattern)
  215.       usage("No pattern");
  216.    if (nfile == 0)
  217.       grep(stdin, 0);
  218.    else {
  219.       fflag = fflag ^ (nfile > 0);
  220.       for (i=1; i < argc; ++i) {
  221.          if (p = argv[i]) {
  222.             if ((f=fopen(p, "r")) == NULL)
  223.                cant(p);
  224.             else {
  225.                grep(f, p);
  226.                fclose(f);
  227.             }
  228.          }
  229.       }
  230.    }
  231. }
  232.  
  233. /*******************************************************/
  234.  
  235. file(s)
  236. char *s;
  237. {
  238.    printf("\nFile %s:\n", s);
  239. }
  240.  
  241. /*******************************************************/
  242.  
  243. cant(s)
  244. char *s;
  245. {
  246.    fprintf(stderr, "%s: cannot open\n", s);
  247. }
  248.  
  249.  
  250. /*******************************************************/
  251.  
  252. help(hp)
  253. char **hp;  /* dns added extra '*'  */
  254. /*
  255.  * Give good help
  256.  */
  257. {
  258.    register char   **dp;
  259.  
  260.    for (dp = hp; *dp; dp++)
  261.       printf("%s\n", *dp);
  262. }
  263.  
  264.  
  265. /*******************************************************/
  266.  
  267. usage(s)
  268. char    *s;
  269. {
  270.    fprintf(stderr, "?GREP-E-%s\n", s);
  271.    fprintf(stderr,
  272.       "Usage: grep [-cfnv] [\"]pattern[\"] [file ...].  grep ? [|more] for help\n\n");
  273.    fprintf(stderr,
  274.       "Copyright (C) 1980 DECUS : copy and modification rights granted by DECUS for\n");
  275.    fprintf(stderr, "not-for-profit use\n\n");
  276.    fprintf(stderr,
  277.       "Adapted for MS-DOS 2.0+ by David P. Babcock, August 1986");
  278.    exit(1);
  279. }
  280.  
  281.  
  282.  
  283. /*******************************************************/
  284.  
  285.  
  286. compile(source)
  287. char       *source;   /* Pattern to compile         */
  288. /*
  289.  * Compile the pattern into global pbuf[]
  290.  */
  291. {
  292.    register char  *s;         /* Source string pointer     */
  293.    register char  *lp;        /* Last pattern pointer      */
  294.    register int   c;          /* Current character         */
  295.    int            o;          /* Temp                      */
  296.    char           *spp;       /* Save beginning of pattern */
  297.    char           *cclass();  /* Compile class routine     */
  298.  
  299. /* allow for quoted string.  Unquote it. /dpb */
  300.    if (source[0]=='"')
  301.      {
  302.      o = strlen(source);
  303.      source[o-1] = 0;
  304.      source++;
  305.      }
  306.  
  307.    s = source;
  308.    if (debug)
  309.       printf("Pattern = \"%s\"\n", s);
  310.  
  311.    pp = pbuf;
  312.    while (c = *s++) {
  313.       /*
  314.        * STAR, PLUS and MINUS are special.
  315.        */
  316.       if (c == '*' || c == '+' || c == '-') {
  317.          if (pp == pbuf ||
  318.               (o=pp[-1]) == BOL ||
  319.               o == EOL ||
  320.               o == STAR ||
  321.               o == PLUS ||
  322.               o == MINUS)
  323.             badpat("Illegal occurrence op.", source, s);
  324.          store(ENDPAT);
  325.          store(ENDPAT);
  326.          spp = pp;               /* Save pattern end     */
  327.          while (--pp > lp)       /* Move pattern down    */
  328.             *pp = pp[-1];        /* one byte             */
  329.          *pp =   (c == '*') ? STAR :
  330.             (c == '-') ? MINUS : PLUS;
  331.          pp = spp;               /* Restore pattern end  */
  332.          continue;
  333.       }
  334.       /*
  335.        * All the rest.
  336.        */
  337.       lp = pp;         /* Remember start       */
  338.       switch(c) {
  339.  
  340.       case '^':
  341.          store(BOL);
  342.          break;
  343.  
  344.       case '$':
  345.          store(EOL);
  346.          break;
  347.  
  348.       case '.':
  349.          store(ANY);
  350.          break;
  351.  
  352.       case '[':
  353.          s = cclass(source, s);
  354.          break;
  355.  
  356.       case ':':
  357.          if (*s) {
  358.             c = *s++;
  359.             switch(tolower(c)) {
  360.  
  361.             case 'a':
  362.             case 'A':
  363.                store(ALPHA);
  364.                break;
  365.  
  366.             case 'd':
  367.             case 'D':
  368.                store(DIGIT);
  369.                break;
  370.  
  371.             case 'n':
  372.             case 'N':
  373.                store(NALPHA);
  374.                break;
  375.  
  376.             case '_':
  377.                store(PUNCT);
  378.                break;
  379.  
  380.             default:
  381.                badpat("Unknown : type", source, s);
  382.  
  383.             }
  384.             break;
  385.          }
  386.          else    badpat("No : type", source, s);
  387.  
  388.       case '\\':
  389.          if (*s)
  390.             c = *s++;
  391.  
  392.       default:
  393.          store(CHAR);
  394.          store(tolower(c));
  395.       }
  396.    }
  397.    store(ENDPAT);
  398.    store(0);                /* Terminate string     */
  399.    if (debug) {
  400.       for (lp = pbuf; lp < pp;) {
  401.          if ((c = (*lp++ & 0377)) < ' ')
  402.             printf("\\%o ", c);
  403.          else    printf("%c ", c);
  404.         }
  405.         printf("\n");
  406.    }
  407. }
  408.  
  409. /*******************************************************/
  410.  
  411. char *
  412. cclass(source, src)
  413. char       *source;   /* Pattern start -- for error msg.      */
  414. char       *src;      /* Class start           */
  415. /*
  416.  * Compile a class (within [])
  417.  */
  418. {
  419.    register char   *s;        /* Source pointer    */
  420.    register char   *cp;       /* Pattern start     */
  421.    register int    c;         /* Current character */
  422.    int             o;         /* Temp              */
  423.  
  424.    s = src;
  425.    o = CLASS;
  426.    if (*s == '^') {
  427.       ++s;
  428.       o = NCLASS;
  429.    }
  430.    store(o);
  431.    cp = pp;
  432.    store(0);                          /* Byte count      */
  433.    while ((c = *s++) && c!=']') {
  434.       if (c == '\\') {                /* Store quoted char    */
  435.          if ((c = *s++) == '\0')      /* Gotta get something  */
  436.             badpat("Class terminates badly", source, s);
  437.          else    store(tolower(c));
  438.       }
  439.       else if (c == '-' &&
  440.             (pp - cp) > 1 && *s != ']' && *s != '\0') {
  441.          c = pp[-1];             /* Range start     */
  442.          pp[-1] = RANGE;         /* Range signal    */
  443.          store(c);               /* Re-store start  */
  444.          c = *s++;               /* Get end char and*/
  445.          store(tolower(c));      /* Store it        */
  446.       }
  447.       else {
  448.          store(tolower(c));      /* Store normal char */
  449.       }
  450.    }
  451.    if (c != ']')
  452.       badpat("Unterminated class", source, s);
  453.    if ((c = (pp - cp)) >= 256)
  454.       badpat("Class too large", source, s);
  455.    if (c == 0)
  456.       badpat("Empty class", source, s);
  457.    *cp = c;
  458.    return(s);
  459. }
  460.  
  461. /*******************************************************/
  462.  
  463. store(op)
  464. {
  465.    if (pp >= &pbuf[PMAX])
  466.       error("Pattern too complex\n");
  467.    *pp++ = op;
  468. }
  469.  
  470.  
  471. /*******************************************************/
  472.  
  473. badpat(message, source, stop)
  474. char  *message;       /* Error message */
  475. char  *source;        /* Pattern start */
  476. char  *stop;          /* Pattern end   */
  477. {
  478.    register int    c;
  479.  
  480.    fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  481.    fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  482.          stop-source, stop[-1]);
  483.    error("?GREP-E-Bad pattern\n");
  484. }
  485.  
  486.  
  487.  
  488. /*******************************************************/
  489.  
  490. grep(fp, fn)
  491. FILE       *fp;       /* File to process            */
  492. char       *fn;       /* File name (for -f option)  */
  493. /*
  494.  * Scan the file for the pattern in pbuf[]
  495.  */
  496. {
  497.    register int lno, count, m;
  498.  
  499.    lno = 0;
  500.    count = 0;
  501.    while (fgets(lbuf, LMAX, fp)) {
  502.       ++lno;
  503.       m = match();
  504.       if ((m && !vflag) || (!m && vflag)) {
  505.          ++count;
  506.          if (!cflag) {
  507.             if (fflag && fn) {
  508.                file(fn);
  509.                fn = 0;
  510.             }
  511.             if (nflag)
  512.                printf("%d\t", lno);
  513. /*          printf("%s\n", lbuf);       */
  514.             printf("%s", lbuf);
  515.          }
  516.       }
  517.    }
  518.    if (cflag) {
  519.       if (fflag && fn)
  520.          file(fn);
  521.       printf("%d\n", count);
  522.    }
  523. }
  524.  
  525.  
  526. /*******************************************************/
  527.  
  528. match()
  529. /*
  530.  * Match the current line (in lbuf[]), return 1 if it does.
  531.  */
  532. {
  533.    register char   *l;        /* Line pointer       */
  534.    char *pmatch();
  535.  
  536.    for (l = lbuf; *l; l++) {
  537.       if (pmatch(l, pbuf))
  538.          return(1);
  539.    }
  540.    return(0);
  541. }
  542.  
  543. /*******************************************************/
  544.  
  545. char *
  546. pmatch(line, pattern)
  547. char               *line;     /* (partial) line to match      */
  548. char               *pattern;  /* (partial) pattern to match   */
  549. {
  550.    register char   *l;        /* Current line pointer         */
  551.    register char   *p;        /* Current pattern pointer      */
  552.    register char   c;         /* Current character            */
  553.    char            *e;        /* End for STAR and PLUS match  */
  554.    int             op;        /* Pattern operation            */
  555.    int             n;         /* Class counter                */
  556.    char            *are;      /* Start of STAR match          */
  557.  
  558.    l = line;
  559.    if (debug > 1)
  560.       printf("pmatch(\"%s\")\n", line);
  561.    p = pattern;
  562.    while ((op = *p++) != ENDPAT) {
  563.       if (debug > 1)
  564.          printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  565.                l-line, *l, *l, op);
  566.       switch(op) {
  567.  
  568.       case CHAR:
  569.          if (tolower(*l++) != *p++)
  570.             return(0);
  571.          break;
  572.  
  573.       case BOL:
  574.          if (l != lbuf)
  575.             return(0);
  576.          break;
  577.  
  578.       case EOL:
  579.          if (*l != '\0')
  580.             return(0);
  581.          break;
  582.  
  583.       case ANY:
  584.          if (*l++ == '\0')
  585.             return(0);
  586.          break;
  587.  
  588.       case DIGIT:
  589.          if ((c = *l++) < '0' || (c > '9'))
  590.             return(0);
  591.          break;
  592.  
  593.       case ALPHA:
  594.          c = tolower(*l++);
  595.          if (c < 'a' || c > 'z')
  596.             return(0);
  597.          break;
  598.  
  599.       case NALPHA:
  600.          c = tolower(*l++);
  601.          if (c >= 'a' && c <= 'z')
  602.             break;
  603.          else if (c < '0' || c > '9')
  604.             return(0);
  605.          break;
  606.  
  607.       case PUNCT:
  608.          c = *l++;
  609.          if (c == 0 || c > ' ')
  610.             return(0);
  611.          break;
  612.  
  613.       case CLASS:
  614.       case NCLASS:
  615.          c = tolower(*l++);
  616.          n = *p++ & 0377;
  617.          do {
  618.             if (*p == RANGE) {
  619.                p += 3;
  620.                n -= 2;
  621.                if (c >= p[-2] && c <= p[-1])
  622.                   break;
  623.             }
  624.             else if (c == *p++)
  625.                break;
  626.          } while (--n > 1);
  627.          if ((op == CLASS) == (n <= 1))
  628.             return(0);
  629.          if (op == CLASS)
  630.             p += n - 2;
  631.          break;
  632.  
  633.       case MINUS:
  634.          e = pmatch(l, p);       /* Look for a match    */
  635.          while (*p++ != ENDPAT); /* Skip over pattern   */
  636.          if (e)                  /* Got a match?        */
  637.             l = e;               /* Yes, update string  */
  638.          break;                  /* Always succeeds     */
  639.  
  640.       case PLUS:                 /* One or more ...     */
  641.          if ((l = pmatch(l, p)) == 0)
  642.             return(0);           /* Gotta have a match  */
  643.       case STAR:                 /* Zero or more ...    */
  644.          are = l;                /* Remember line start */
  645.          while (*l && (e = pmatch(l, p)))
  646.             l = e;               /* Get longest match   */
  647.          while (*p++ != ENDPAT); /* Skip over pattern   */
  648.          while (l >= are) {      /* Try to match rest   */
  649.             if (e = pmatch(l, p))
  650.                return(e);
  651.             --l;                 /* Nope, try earlier   */
  652.          }
  653.          return(0);              /* Nothing else worked */
  654.  
  655.       default:
  656.          printf("Bad op code %d\n", op);
  657.          error("Cannot happen -- match\n");
  658.       }
  659.    }
  660.    return(l);
  661. }
  662.  
  663. /*******************************************************/
  664.  
  665. error(s)
  666. char *s;
  667. {
  668.    fprintf(stderr, "%s", s);
  669.    exit(1);
  670. }
  671.  
  672. /*******************************************************/
  673.  
  674. #ifdef DeSmet
  675. fclose(file)
  676. FILE file;
  677. {
  678.      return(close(file));
  679. }
  680. #endif
  681.  
  682.